﻿using Microsoft.Extensions.DependencyInjection;
using MiniJob.Worker.Jobs;

namespace MiniJob.Worker.Runtime;

internal class DependencyInjectionJobActivator : JobActivator
{
    private readonly IServiceProvider services;
    private readonly JobTypeInformation type;
    private readonly Func<ObjectFactory> initializer;

    // 这用作从依赖注入（DI）构建对象的已生成代码的缓存
    private ObjectFactory? factory;
    private bool initialized;
    private object? @lock;

    public DependencyInjectionJobActivator(IServiceProvider services, JobTypeInformation type)
    {
        this.services = services;
        this.type = type;

        initializer = () =>
        {
            return ActivatorUtilities.CreateFactory(this.type.ImplementationType, []);
        };
    }

    public override async Task<JobActivatorState> CreateAsync()
    {
        var scope = services.CreateScope();
        try
        {
            var factory = LazyInitializer.EnsureInitialized(
                ref this.factory,
                ref this.initialized,
                ref this.@lock,
            this.initializer);

            var job = (Job)factory!(scope.ServiceProvider, []);
            job.ServiceProvider = scope.ServiceProvider;
            return new State(job, scope);
        }
        catch
        {
            // Make sure to clean up the scope if we fail to create;
            await DisposeCore(scope);
            throw;
        }
    }

    public override async Task DeleteAsync(JobActivatorState obj)
    {
        var state = (State)obj;
        await DisposeCore(state.Job);
        await DisposeCore(state.Scope);
    }

    private async ValueTask DisposeCore(object obj)
    {
        if (obj is IAsyncDisposable asyncDisposable)
        {
            await asyncDisposable.DisposeAsync();
        }
        else if (obj is IDisposable disposable)
        {
            disposable.Dispose();
        }
    }

    public class State(Job job, IServiceScope scope) : JobActivatorState(job)
    {
        public IServiceScope Scope { get; } = scope;
    }
}
